
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0+
 * License-Filename: LICENSE
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "splay-tree.h"
#include "main.h"
#include "parse.h"
#include "parsedot.h"
#include "uniqstring.h"
#include "color.h"
#include "options.h"
#include "nes.h"

/* start of subgraph "name" { } */
/* rank is attr. for subgraph */
int is_a_dotsubgraph(struct usubg *subg)
{
	struct usubg *newsg = NULL;
	char *sgname = NULL;
	char buf[32];
	int ret = 0;

	/* subgraph or SUBGRAPH allowed */
	if (token != DOT_SUBGRAPH) {
		return (0);
	}

	/* subgraphname or "subgraphname" or nothing */
	token = dot_lex();
	if (token == DOT_ID) {
		/* check for brace open */
		sgname = lastid;
		token = dot_lex();
		if (token != DOT_BO) {
			parser_error_string = " expected brace open";
			return (0);
		}
	} else if (token == DOT_STRING) {
		/* check for brace open */
		sgname = laststring;
		token = dot_lex();
		if (token != DOT_BO) {
			parser_error_string = " expected brace open";
			return (0);
		}
	} else if (token == DOT_NUM) {
		/* check for brace open */
		sgname = lastnum;
		token = dot_lex();
		if (token != DOT_BO) {
			parser_error_string = " expected brace open";
			return (0);
		}
	} else if (token == DOT_CHAR) {
		/* check for brace open */
		sgname = lastchar;
		token = dot_lex();
		if (token != DOT_BO) {
			parser_error_string = "expected brace open at start of subgraph";
			return (0);
		}
	} else if (token == DOT_BO) {
		/* it is subgraph { ... } and create a name for subgraph */
		memset(buf, 0, 32);
		snprintf(buf, (32 - 1), "__subgraph%d__", nsubgraph);
		sgname = uniqstring(buf);
	} else {
		parser_error_string = "expected subgraph \"name\" { ... } or subgraph { ... }";
		dot_unlex();
		return (1);
	}

	if (sgname == NULL) {
		parser_error_string = "nil subgraph name, expected id, \"string\" or number as subgraph name";
		return (1);
	}

	/* subgraph "" { ... } is not accepted */
	if (strlen(sgname) == 0) {
		parser_error_string = "empty subgraph name, expected id, \"string\" or number as subgraph name";
		return (1);
	}

	/* create subgraph with name rooted on subg */
	newsg = usubg_new(sgname, subg);

	/* copy node, edge defaults */
	set_subg_defaults(subg, newsg);

	/* dot checks for cluster subgraphs */
	if (strncmp(sgname, "cluster", 7) == 0) {
		newsg->bitflags.cluster = 1;
	} else {
		newsg->bitflags.cluster = 0;
	}

	/* create label for subgraph */
	memset(buf, 0, 32);

	/* in display start counting subgraph with 1 */
	snprintf(buf, (32 - 1), "subgraph%d", (nsubgraph + 1));

	/* initial subgraph label can be changed later by label statement */
	newsg->label = uniqstring(buf);

	/* label of summary node */
	if (newsg->nd_labeltype) {
		newsg->label = newsg->nd_label;
	} else {
		newsg->summaryn->label = sgname;	/* set to subgraph name, or can be newsg->label; */
	}

	/* create uniq node name for summary node */
	memset(buf, 0, 32);
	snprintf(buf, (32 - 1), "__subgraph%d_%p__", nsubgraph, (void *)newsg);

	newsg->summaryn->name = uniqstring(buf);

	/* update count */
	nsubgraph = nsubgraph + 1;
	/* initially unfolded subgraph */
	newsg->bitflags.folded = 0;
	/* fold/unfold depending on command line option */
	if (option_init_unfolded) {
		newsg->bitflags.folded = 0;
	}
	if (option_init_folded) {
		newsg->bitflags.folded = 1;
	}
	/* add subgraph to list of subgraphs */
	parsedsgl_add(newsg);
	/* configure summary node */
	/* summary node is part of new subgraph */
	newsg->summaryn->rootedon = newsg;
	/* white background color */
	newsg->summaryn->fillcolor = 0x00ffffff;
	/* this is a subgraph summary node */
	newsg->summaryn->bitflags.sumnode = 1;
	/* add summary node to list of summary nodes */
	summarynl_add(newsg->summaryn);
	/* set which is the starting subgraph in root graph */
	if (subg == NULL) {
		/* started in root graph */
		newsg->rootsubgraph = newsg;
		newsg->rootedon = NULL;
	} else {
		/* is a subsubgraph */
		newsg->rootsubgraph = subg->rootsubgraph;
	}
	/* check if there is a pending edge from a subgraph to this subgraph */
	if (pe_lasttoken) {
		/* */
		if (option_parsedebug) {
			printf("%s(): subgraph=%s pe_lasttoken found lastnode=%p lastsubg=%p\n", __FUNCTION__, newsg->label,
			       (void *)pe_lastnode, (void *)pe_lastsubg);
			fflush(stdout);
		}

		/* edge from subgraph to node */
		if (pe_lastsubg) {
			dot_desadd(NULL, pe_lastsubg, NULL, newsg, DES_S_S);
		}

		/* clear because data is handled */
		dot_pe_clear();
	}

	/* parse subgraph data until the last brace close, return status 0/1 */
	ret = parsedot2(newsg);

	if (option_parsedebug) {
		printf("%s(): status %d from parsedot2()\n", __FUNCTION__, ret);
		fflush(stdout);
	}

	/* check parse status */
	if (ret) {
		/* parsed oke and set flag for edge */
		pe_lastsubg = newsg;
		pe_lastnode = (struct unode *)0;
	} else {
		/* parse error */
		dot_pe_clear();
	}

	/* return 1/0 */
	return (ret);
}

/* end */
